package org.activityinfo.server.endpoint.rest;
/*
* #%L
* ActivityInfo Server
* %%
* Copyright (C) 2009 - 2013 UNICEF
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
import com.bedatadriven.geojson.GeometrySerializer;
import com.google.common.base.Charsets;
import com.sun.jersey.api.core.InjectParam;
import com.sun.jersey.api.view.Viewable;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.io.ParseException;
import org.activityinfo.model.auth.AuthenticatedUser;
import org.activityinfo.server.database.hibernate.entity.*;
import org.activityinfo.server.endpoint.rest.model.*;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.util.DefaultPrettyPrinter;
import javax.inject.Provider;
import javax.persistence.EntityManager;
import javax.persistence.FlushModeType;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;
public class AdminLevelResource {
private static final Logger LOGGER = Logger.getLogger(AdminLevelResource.class.getName());
private Provider<EntityManager> entityManager;
private AdminLevel level;
// TODO: create list of geoadmins per country
private static final int SUPER_USER_ID = 3;
public AdminLevelResource(Provider<EntityManager> entityManager, AdminLevel level) {
super();
this.entityManager = entityManager;
this.level = level;
}
@GET @Produces(MediaType.TEXT_HTML)
public Viewable get() {
return new Viewable("/resource/AdminLevel.ftl", level);
}
@GET @Produces(MediaType.APPLICATION_JSON)
public AdminLevel getJson() {
return level;
}
@DELETE
public Response deleteLevel(@InjectParam AuthenticatedUser user) {
assertAuthorized(user);
EntityManager em = entityManager.get();
em.getTransaction().begin();
AdminLevel level = entityManager.get().merge(this.level);
level.setDeleted(true);
em.getTransaction().commit();
return Response.ok().build();
}
private void assertAuthorized(AuthenticatedUser user) {
if (user.getId() != SUPER_USER_ID) {
throw new WebApplicationException(Status.FORBIDDEN);
}
}
@GET @Path("/entities") @Produces(MediaType.APPLICATION_JSON)
public List<AdminEntity> getEntities(@InjectParam EntityManager em) {
return em.createQuery("select e from AdminEntity e where e.deleted = false and e.level = :level")
.setParameter("level", level)
.getResultList();
}
@GET @Path("/entities/features")
public Response getFeatures(@InjectParam EntityManager em) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(baos, Charsets.UTF_8);
List<AdminEntity> entities = em.createQuery(
"select e from AdminEntity e where e.deleted = false and e.level = :level")
.setParameter("level", level)
.getResultList();
JsonFactory jfactory = new JsonFactory();
JsonGenerator json = jfactory.createJsonGenerator(writer);
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
json.setPrettyPrinter(prettyPrinter);
json.writeStartObject();
json.writeStringField("type", "FeatureCollection");
json.writeFieldName("features");
json.writeStartArray();
GeometrySerializer geometrySerializer = new GeometrySerializer();
for (AdminEntity entity : entities) {
if (entity.getGeometry() != null) {
json.writeStartObject();
json.writeStringField("type", "Feature");
json.writeNumberField("id", entity.getId());
json.writeObjectFieldStart("properties");
json.writeStringField("name", entity.getName());
if (entity.getParentId() != null) {
json.writeNumberField("parentId", entity.getParentId());
}
json.writeEndObject();
json.writeFieldName("geometry");
geometrySerializer.writeGeometry(json, entity.getGeometry());
json.writeEndObject();
}
}
json.writeEndArray();
json.writeEndObject();
json.close();
return Response.ok().entity(baos.toByteArray()).type(MediaType.APPLICATION_JSON).build();
}
@PUT @Consumes(MediaType.APPLICATION_JSON)
public Response update(@InjectParam AuthenticatedUser user, UpdatedAdminLevel updatedLevel) throws ParseException {
assertAuthorized(user);
EntityManager em = entityManager.get();
em.getTransaction().begin();
AdminLevel level = entityManager.get().merge(this.level);
level.setName(updatedLevel.getName());
for (LocationType boundLocationType : level.getBoundLocationTypes()) {
boundLocationType.setName(updatedLevel.getName());
}
if (updatedLevel.getEntities() != null) {
for (UpdatedAdminEntity updatedEntity : updatedLevel.getEntities()) {
// check geometry
if (updatedEntity.getGeometry() != null && !isValid(updatedEntity.getGeometry())) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST)
.entity("Geometry must be Polygon or MultiPolygon")
.build());
}
if (updatedEntity.isDeleted()) {
// mark the entity as deleted. we can't remove it from
// the database because we may have locations which refer to it
// on distant clients
em.find(AdminEntity.class, updatedEntity.getId()).setDeleted(true);
} else if (updatedEntity.isNew()) {
// create new entity
AdminEntity entity = new AdminEntity();
entity.setLevel(level);
if (updatedEntity.getParentId() != null) {
entity.setParent(em.getReference(AdminEntity.class, updatedEntity.getParentId()));
}
entity.setName(updatedEntity.getName());
entity.setCode(updatedEntity.getCode());
entity.setBounds(updatedEntity.getBounds());
entity.setGeometry(updatedEntity.getGeometry());
em.persist(entity);
} else {
// update existing entity
// TODO: bound locations that share this name?
AdminEntity entity = em.find(AdminEntity.class, updatedEntity.getId());
entity.setName(updatedEntity.getName());
entity.setCode(updatedEntity.getCode());
entity.setBounds(updatedEntity.getBounds());
entity.setGeometry(updatedEntity.getGeometry());
}
}
}
int newVersion = level.getVersion() + 1;
level.setVersion(newVersion);
AdminLevelVersion version = new AdminLevelVersion();
version.setLevel(level);
version.setVersion(newVersion);
version.setUser(em.getReference(User.class, user.getId()));
version.setTimeCreated(new Date().getTime());
VersionMetadata metadata = updatedLevel.getVersionMetadata();
if (metadata != null) {
version.setSourceUrl(metadata.getSourceUrl());
version.setSourceFilename(metadata.getSourceFilename());
version.setSourceHash(metadata.getSourceMD5());
version.setMessage(metadata.getMessage());
version.setSourceMetadata(metadata.getSourceMetadata());
}
em.persist(version);
em.getTransaction().commit();
return Response.ok().build();
}
private boolean isValid(Geometry geometry) {
return geometry instanceof Polygon || geometry instanceof MultiPolygon;
}
@POST @Path("/childLevels") @Consumes(MediaType.APPLICATION_JSON)
public Response postNewLevel(@InjectParam AuthenticatedUser user, NewAdminLevel newLevel) throws ParseException {
assertAuthorized(user);
EntityManager em = entityManager.get();
em.getTransaction().begin();
em.setFlushMode(FlushModeType.COMMIT);
AdminLevel child = new AdminLevel();
child.setCountry(level.getCountry());
child.setName(newLevel.getName());
child.setParent(level);
em.persist(child);
for (NewAdminEntity entity : newLevel.getEntities()) {
AdminEntity childEntity = new AdminEntity();
childEntity.setName(entity.getName());
childEntity.setLevel(child);
childEntity.setCode(entity.getCode());
childEntity.setBounds(entity.getBounds());
childEntity.setParent(em.getReference(AdminEntity.class, entity.getParentId()));
childEntity.setGeometry(entity.getGeometry());
child.getEntities().add(childEntity);
em.persist(childEntity);
}
// create bound location type
LocationType boundType = new LocationType();
boundType.setBoundAdminLevel(child);
boundType.setCountry(level.getCountry());
boundType.setName(child.getName());
em.persist(boundType);
em.getTransaction().commit();
return Response.ok().build();
}
}